package org.example.skytool.redis;

import cn.hutool.core.util.BooleanUtil;
import lombok.extern.slf4j.Slf4j;
import org.example.exception.ServerErrorException;
import org.example.skytool.commom.BeanUtils;
import org.example.skytool.commom.StringUtil;
import org.example.skytool.json.JackSonUtil;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author 22449
 */
@Slf4j
@Component
public class CacheClient {

    @Resource
    private RedisUtils redisUtils;

    public <R> R queryString(
            String cacheKey, Class<R> type,
            Map param,
            String lockKey, Function<Map, R> dbFallback, Long time, TimeUnit unit,
            Long nullTtl) {
        String json = redisUtils.stringGet(cacheKey);
        if (!StringUtil.isEmpty(json)) {
            return JackSonUtil.parse(type, json);
        }
        if (json == null) {
            R r = null;
            try {
                boolean isLock = tryLock(lockKey);
                if (!isLock) {
                    Thread.sleep(50);
                    return queryString(
                            cacheKey, type,
                            param,
                            lockKey, dbFallback, time, unit,
                            nullTtl);
                }
                r = dbFallback.apply(param);
                if (r == null) {
                    redisUtils.stringSet(cacheKey, "", nullTtl, TimeUnit.MINUTES);
                    return null;
                }
                json = JackSonUtil.encode(r);
                redisUtils.stringSet(cacheKey, json, time, unit);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                unlock(lockKey);
            }
            return r;
        }
        if ("".equals(json)) {
            return null;
        }
        throw new ServerErrorException("非常严重的逻辑异常");
    }

    /**
     * 参数分别为：
     * redis执行需要的参数
     * 执行回退需要的参数
     * 有关时间的参数
     */
    public <R> R queryHash(
            String cacheKey, Class<R> type,
            Map param, String lockKey, Function<Map, R> dbFallback,
            Long time, TimeUnit unit, Long nullTtl) {


        // 从redis查询缓存
        Map map = redisUtils.hashGetAll(cacheKey);


        //如果命中的是 空 那就返回null
        if ("".equals(map.get("emptyJudge"))) {
            return null;
        }

        // 判断是否存在
        if (map != null && map.size() != 0) {
            // 存在，直接返回
            return BeanUtils.mapToBean(type, map);
        }


        //如果命中的是 null 那就构造缓存
        if (map == null || map.size() == 0) {

            // 实现缓存重建
            R r = null;
            try {
                // 获取互斥锁
                boolean isLock = tryLock(lockKey);
                // 判断是否获取成功
                if (!isLock) {
                    // 获取锁失败，休眠并重试
                    Thread.sleep(50);
                    return queryHash(
                            cacheKey, type,
                            param,
                            lockKey, dbFallback, time, unit,
                            nullTtl);
                }

                // 获取锁成功，执行构造语句
                r = dbFallback.apply(param);

                // 不存在，返回错误
                if (r == null) {
                    //将空值写入redis
                    redisUtils.hashPut(cacheKey, "emptyJudge", "", nullTtl, unit);
                    //返回错误信息
                    return null;
                }

                map = BeanUtils.beanToMap(r);
                // 存在，写入redis
                redisUtils.hashPut(cacheKey, map, time, unit);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                // 释放锁
                unlock(lockKey);
            }

            // 8.返回
            return r;
        }

        throw new ServerErrorException("非常严重的逻辑异常");

    }

    public void hasSetKey(
            String cacheKey,
            Map param,
            String lockKey, Function<Map, Long> dbFallback, Long time, TimeUnit unit,
            Long nullTtl) {


        Long size = redisUtils.setSize(cacheKey);

        //如果命中的是 空 那就返回null
        if (redisUtils.setIsMember(cacheKey, "judgeEmpty") && size == 1) {

            return;
        }

        //缓存存在 直接返回
        if (size != 0) {

            return;
        }

        //如果命中的是 null 那就构造缓存
        if (size == 0) {

            // 实现缓存重建
            try {
                // 获取互斥锁
                boolean isLock = tryLock(lockKey);
                // 判断是否获取成功
                if (!isLock) {
                    // 获取锁失败，休眠并重试
                    Thread.sleep(50);
                    hasSetKey(
                            cacheKey,
                            param,
                            lockKey, dbFallback, time, unit,
                            nullTtl);
                    return;
                }

                // 获取锁成功，执行构造语句
                size = dbFallback.apply(param);

                // 不存在，返回错误
                if (size == 0) {
                    //将空值写入redis
                    redisUtils.setAddOne(cacheKey, "emptyJudge", nullTtl, unit);
                    //返回错误信息
                    return;
                }

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                // 释放锁
                unlock(lockKey);
            }

            // 8.返回
            return;
        }

        throw new ServerErrorException("非常严重的逻辑异常");
    }

    public <R> List<String> queryList(
            String cacheKey, Integer start, Integer limit,
            Map param,
            String lockKey, Function<Map, Long> dbFallback, Long time, TimeUnit unit,
            Long nullTtl) {

        // 从redis查询缓存
        List<String> list = redisUtils.listGet(cacheKey, start, limit);

        //如果命中的是 空 那就返回null
        if (list.size() == 1 && "emptyJudge".equals(list.get(0))) {

            return null;
        }

        // 判断是否存在
        if (list != null&&list.size()!=0) {

            return list;
        }

        //如果命中的是 null 那就构造缓存
        if (list.size()==0||list == null) {

            // 实现缓存重建
            R r = null;
            try {
                // 获取互斥锁
                boolean isLock = tryLock(lockKey);
                // 判断是否获取成功
                if (!isLock) {
                    // 获取锁失败，休眠并重试
                    Thread.sleep(50);
                    return queryList(
                            cacheKey, start, limit,
                            param,
                            lockKey, dbFallback, time, unit,
                            nullTtl);
                }

                // 获取锁成功，执行构造语句
                Long num = dbFallback.apply(param);


                // 不存在，返回错误
                if (num == 0) {
                    //将空值写入redis
                    redisUtils.setAddOne(cacheKey, "emptyJudge", nullTtl, unit);
                    //返回错误信息
                    return null;
                }

                // 存在，重新查询
                return queryList(
                        cacheKey, start, limit,
                        param,
                        lockKey, dbFallback, time, unit,
                        nullTtl);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                // 释放锁
                unlock(lockKey);
            }

        }

        throw new ServerErrorException("非常严重的逻辑异常");

    }


    private boolean tryLock(String key) {
        Boolean flag = redisUtils.stringSetNx(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }

    private void unlock(String key) {
        redisUtils.del(key);
    }


}